﻿using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.Hotspots;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.LiveTemplates;
using JetBrains.ReSharper.LiveTemplates;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.TextControl;

namespace MugenMvvmToolkit.ReSharper.Infrastructure
{
    public class ModelPropertyGenerator
    {
        #region Fields

        private readonly CSharpElementFactory _elementFactory;
        private IClassLikeDeclaration _currentClassDeclaration;
        private IClass _modelClass;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="ModelPropertyGenerator" /> class.
        /// </summary>
        public ModelPropertyGenerator([NotNull] CSharpElementFactory elementFactory)
        {
            if (elementFactory == null) throw new ArgumentNullException("elementFactory");
            _elementFactory = elementFactory;
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="ModelPropertyGenerator" /> class.
        /// </summary>
        public ModelPropertyGenerator([NotNull] IClassLikeDeclaration classLikeDeclaration, [NotNull] IClass modelClass,
            [NotNull] CSharpElementFactory elementFactory)
            : this(elementFactory)
        {
            Update(classLikeDeclaration, modelClass);
        }

        #endregion

        #region Properties

        public bool IsEmpty
        {
            get { return _currentClassDeclaration == null || _modelClass == null; }
        }

        public IEnumerable<IProperty> MissingProperties
        {
            get { return GetMissingProperties(_currentClassDeclaration, _modelClass); }
        }

        #endregion

        #region Static methods

        public static IEnumerable<IProperty> GetMissingProperties(IClassLikeDeclaration currentClassDeclaration,
            [CanBeNull]IClass modelClass)
        {
            if (modelClass == null)
                yield break;
            foreach (IProperty property in modelClass.Properties)
            {
                if (ToolkitExtensions.IgnoreProperties.Contains(property.ShortName) ||
                    property.GetAccessRights() != AccessRights.PUBLIC || !property.IsWritable ||
                    !property.IsReadable)
                    continue;
                if (!currentClassDeclaration
                    .PropertyDeclarations
                    .Any(declaration => declaration.DeclaredName == property.ShortName))
                    yield return property;
            }
        }

        #endregion

        #region Methods

        public void Update([NotNull] IClassLikeDeclaration classLikeDeclaration, [NotNull] IClass modelClass)
        {
            if (classLikeDeclaration == null) throw new ArgumentNullException("classLikeDeclaration");
            if (modelClass == null) throw new ArgumentNullException("modelClass");
            _currentClassDeclaration = classLikeDeclaration;
            _modelClass = modelClass;
        }

        public Action<ISolution, LiveTemplatesManager, ITextControl> Generate(bool withNotification,
            bool wrapToRegion, IEnumerable<IProperty> properties = null)
        {
            string pattern = withNotification
                ? CodeTemplateConstants.ModelPropertyWithNotifyPropertyChangedTemplate
                : CodeTemplateConstants.ModelPropertyTemplate;
            if (properties == null)
                properties = MissingProperties;
            var newProperties = new List<IPropertyDeclaration>();
            foreach (IProperty missingProperty in properties)
            {
                var newProperty = (IPropertyDeclaration)_elementFactory
                    .CreateTypeMemberDeclaration(pattern, missingProperty.Type, missingProperty.ShortName);
                newProperties.Add(_currentClassDeclaration.AddClassMemberDeclaration(newProperty));
            }

            if (wrapToRegion)
                new RegionWrapper(_currentClassDeclaration, UiResources.RegionModelProperties, _elementFactory)
                    .Wrap(newProperties);

            return (solution, liveTemplatesManager, control) =>
            {
                var hotspots = new HotspotInfo[newProperties.Count];
                for (int index = 0; index < newProperties.Count; index++)
                {
                    IPropertyDeclaration property = newProperties[index];
                    var info = new HotspotInfo(
                        new TemplateField(string.Format("RENAME{0}", property.DeclaredName),
                            new NameSuggestionsExpression(new[] { property.DeclaredName }), 0),
                        property.NameIdentifier.GetDocumentRange());
                    hotspots[index] = info;
                }
                liveTemplatesManager.TryExecuteHotspotDefault(solution, control, hotspots);
            };
        }

        #endregion
    }
}